package org.xtext.runtime.plan;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.xtext.runtime.interpreter.Agent;
import org.xtext.runtime.term.ITerm;
import org.xtext.runtime.term.ListConstructor;
import org.xtext.runtime.term.Variable;
import org.xtext.runtime.type.TypeException;
import org.xtext.runtime.util.Bindings;
import org.xtext.runtime.util.EvaluateTermsVisitor;

public class TypedVariableStep implements IPlanStep {
	private Variable variable;
	private ITerm value;
	
	public TypedVariableStep(Variable variable) {
		this(variable, null);
	}

	public TypedVariableStep(Variable variable, ITerm value) {
		this.variable = variable;
		this.value = value;
	}

	@Override
	public IPlanStep apply(Bindings set) {
		if (Variable.class.isInstance(value)) {
			value = set.getStoredValue((Variable) value);
		} else if (value != null) {
			value.apply(set);
		}
		return this;
	}

	@Override
	public void handle(Agent agent, PlanStack planStack) {
		if (value != null) {
			ITerm val = (ITerm) value.accept(new EvaluateTermsVisitor(agent));
			
			try {
				if (ListConstructor.class.isInstance(val)) {
					variable.assign(((ListConstructor) val).construct(agent));
				} else {
					variable.assign(val);
				}
			} catch (TypeException e) {
				agent.logAction(toString("") + " [FAILURE: COULD NOT ASSIGN VALUE]");
				planStack.fail();
				e.printStackTrace();
				return;
			}
		}
		
		agent.logAction(toString("") + " [SUCCESS]");
	}

	@Override
	public String toString(String tabs) {
		if (value != null) return tabs + variable + " = " + value;
		return tabs+variable;
	}

	public String toFullString(String tabs) {
		return toString(tabs);
	}
	
	@Override
	public Set<Variable> variables() {
		Set<Variable> variables = new HashSet<Variable>();
		variables.add(variable);
		return variables;
	}

	@Override
	public IPlanStep copy(Map<String, Variable> vars) {
		Variable var = vars.get(variable.identifier());
		if (var == null) {
			var = variable.copy();
			vars.put(var.identifier(), var);
		}
		
		if (value == null) {
			return new TypedVariableStep(var);
		}
		
		return new TypedVariableStep(var, value.copy(vars));
	}

	@Override
	public void reset() {
	}
}
